﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;

namespace CustomFtpLib  //FTP协议封装类
{
    public class FtpHelper
    {
        private string ftpServerIP;
        private string ftpRemotePath;
        private string ftpUserID;
        private string ftpPassword;
        private string ftpURI;
        /// <summary>
        /// 连接FTP
        /// </summary>
        /// <param name="FtpServerIP">FTP连接地址</param>
        /// <param name="FtpRemotePath">指定FTP连接成功后的当前目录, 如果不指定即默认为根目录</param>
        /// <param name="FtpUserID">用户名</param>
        /// <param name="FtpPassword">密码</param>
        public FtpHelper(string ServerIP, string RemotePath, string UserID, string Password)
        {
            ftpServerIP = ServerIP;
            ftpRemotePath = RemotePath;
            ftpUserID = UserID;
            ftpPassword = Password;
            ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
        }

        /// <summary>
        /// 上传本地文件srcFileName到服务器当前目录下
        /// </summary>
        /// <param name="filename">文件名</param>
        public string UploadFile(string srcFileName)
        {
            FileInfo fileInfo = new FileInfo(srcFileName);
            string uri = ftpURI + fileInfo.Name;
            FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
            reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
            reqFTP.KeepAlive = false;
            reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
            reqFTP.UseBinary = true;
            reqFTP.UsePassive = false;
            reqFTP.ContentLength = fileInfo.Length;
            try
            {
                int BufferSize = 4096;
                byte[] buff = new byte[BufferSize];
                int ContentLength = 0;
                long currentCounts = 0;
                FileStream fileStream = fileInfo.OpenRead();
                Stream ftpStream = reqFTP.GetRequestStream();

                while (true)
                {
                    ContentLength = fileStream.Read(buff, 0, BufferSize);
                    currentCounts += ContentLength;
                    if (0 == ContentLength)
                        break;
                    ftpStream.Write(buff, 0, ContentLength);
                }

                ftpStream.Close();
                fileStream.Close();
                return "done";
            }
            catch (Exception ex)
            {
                return "FtpHelper Upload Error --> " + ex.Message;
            }
        }
        /// <summary>
        /// 上传本地文件srcFileName到服务器，文件在服务器的路径为objPath，支持断点续传
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <param name="objPath">目标路径</param>
        /// <param name="coverExist">是否覆盖原有文件</param>
        public string UploadFile(string srcFileName, string objPath, bool coverExist)
        {
            try
            {
                if (!objPath.Equals(string.Empty))
                    MakeDirs(objPath);//先创建并进入指定目录，再上传
                FileInfo fileInfo = new FileInfo(srcFileName);
                string uri = ftpURI + fileInfo.Name;
                FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                reqFTP.KeepAlive = false;
                reqFTP.Method = WebRequestMethods.Ftp.AppendFile;
                reqFTP.UseBinary = true;
                reqFTP.UsePassive = false;
                reqFTP.ContentLength = fileInfo.Length;

                long currentCounts = 0;//已上传的字节数
                FileStream fileStream = fileInfo.OpenRead();
                //先检查当前目录有没有存在同名文件
                long currSize = GetFileSize(fileInfo.Name);//文件不存在时返回-1
                if (currSize == fileInfo.Length && !coverExist)//服务器现有文件已完整并且指定不覆盖时则不上传
                    return string.Format("done，当前文件大小：{0}，目标文件大小：{1}", currSize, fileInfo.Length);
                if (currSize > 0 && !coverExist)//现有文件大小大于0并且指定不覆盖时为断点续传
                {
                    fileStream.Seek(currSize, 0);//文件流移动到目标位置
                    currentCounts = currSize;
                }
                Stream ftpStream = reqFTP.GetRequestStream();
                int ContentLength = 0;//每次读到的字节数
                int BufferSize = 4096;
                byte[] buff = new byte[BufferSize];
                while (true)
                {
                    ContentLength = fileStream.Read(buff, 0, BufferSize);
                    currentCounts += ContentLength;
                    if (0 == ContentLength)
                        break;
                    ftpStream.Write(buff, 0, ContentLength);
                    //Console.WriteLine(string.Format("上传进度：{0}/{1}", currentCounts, fileInfo.Length));
                }

                ftpStream.Close();
                fileStream.Close();
                return "done";
            }
            catch (Exception ex)
            {
                return "FtpHelper Upload Error --> " + ex.Message;
            }
        }
        /// <summary>
        /// 下载文件，支持多级路径或服务器完整文件路径
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="fileName">文件名</param>
        public string DownloadFile(string FileName, string LocalSavePath)
        {
            try
            {
                FtpWebRequest reqFTP;
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpUserID + ":" + ftpPassword + "@" + ftpServerIP + "/" + FileName));
                reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
                reqFTP.UseBinary = true;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                reqFTP.UsePassive = false;
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                Stream ftpStream = response.GetResponseStream();
                long cl = response.ContentLength;
                int bufferSize = 2048;
                int readCount;
                byte[] buffer = new byte[bufferSize];
                readCount = ftpStream.Read(buffer, 0, bufferSize);
                FileStream outputStream = new FileStream(LocalSavePath, FileMode.Create);
                while (readCount > 0)
                {
                    outputStream.Write(buffer, 0, readCount);
                    readCount = ftpStream.Read(buffer, 0, bufferSize);
                }
                ftpStream.Close();
                outputStream.Close();
                response.Close();
                return "done";
            }
            catch (Exception ex)
            {
                return "FtpHelper Download Error --> " + ex.Message;
            }
        }

        /// <summary>
        /// 删除文件夹
        /// </summary>
        /// <param name="folderName">文件夹名</param>
        public void RemoveDirectory(string folderName)
        {
            try
            {
                string uri = ftpURI + folderName;
                FtpWebRequest reqFTP;
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));

                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                reqFTP.KeepAlive = false;
                reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory;
                reqFTP.UsePassive = false;

                string result = String.Empty;
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                long size = response.ContentLength;
                Stream datastream = response.GetResponseStream();
                StreamReader sr = new StreamReader(datastream);
                result = sr.ReadToEnd();
                sr.Close();
                datastream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw new Exception("FtpHelper Delete Error --> " + ex.Message + "  文件名:" + folderName);
            }
        }
        /// <summary>
        /// 删除当前目录下的文件
        /// </summary>
        /// <param name="folderName">文件名</param>
        public string deleteFile(string fileName)
        {
            FtpWebRequest reqFTP;
            try
            {
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileName));
                reqFTP.Method = WebRequestMethods.Ftp.DeleteFile;
                reqFTP.UsePassive = false;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                string msg = reader.ReadToEnd();
                reader.Close();
                response.Close();
                return "done" + msg;
            }
            catch (Exception ex)
            {
                return "删除失败，详情：\n" + ex.Message;
            }
        }
        /// <summary>
        /// 获取当前目录下明细(包含文件、文件大小、创建时间、文件夹)
        /// </summary>
        /// <returns>名称数组</returns>
        public string[] GetDetailList()
        {
            List<string> result = new List<string>();
            try
            {
                FtpWebRequest ftp;
                ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
                ftp.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                ftp.UsePassive = false;
                WebResponse response = ftp.GetResponse();
                StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

                string line = reader.ReadLine();
                while (line != null)
                {
                    result.Add(line);
                    line = reader.ReadLine();
                }
                reader.Close();
                response.Close();
                return result.ToArray();
            }
            catch (Exception ex)
            {
                throw new Exception("FtpHelper  Error --> " + ex.Message);
            }
        }
        /// <summary>
        /// 获取当前目录下文件列表(文件名+文件夹名，只有名称)
        /// </summary>
        /// <returns></returns>
        public string[] GetSubList(string mask)
        {
            List<string> result = new List<string>();
            FtpWebRequest reqFTP;
            try
            {
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
                reqFTP.UseBinary = true;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
                reqFTP.UsePassive = false;
                WebResponse response = reqFTP.GetResponse();
                StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

                string line = reader.ReadLine();
                while (line != null)
                {
                    result.Add(line);
                    line = reader.ReadLine();
                }
                reader.Close();
                response.Close();
                return result.ToArray();
            }
            catch (Exception ex)
            {
                if (ex.Message.Trim() != "远程服务器返回错误: (550) 文件不可用(例如，未找到文件，无法访问文件)。")
                {
                    throw new Exception("FtpHelper GetFileList Error --> " + ex.Message.ToString());
                }
                return result.ToArray();
            }
        }
        /// <summary>
        /// 获取当前目录下文件列表(仅文件)
        /// </summary>
        /// <returns></returns>
        public string[] GetFileList()
        {
            string[] temps = GetDetailList();
            List<string> result = new List<string>();
            foreach (string temp in temps)
            {
                if (temp.Contains("<DIR>"))
                    continue;
                int index = temp.LastIndexOf(' ');
                result.Add(temp.Substring(index + 1));
            }
            return result.ToArray();
        }
        /// <summary>
        /// 获取当前目录下所有的文件夹列表(仅文件夹)
        /// </summary>
        /// <returns></returns>
        public string[] GetDirectoryList()
        {
            List<string> result = new List<string>();
            string[] drectory = GetDetailList();
            foreach (string str in drectory)
            {
                int index = str.LastIndexOf("<DIR>");
                if (index == -1)
                    continue;
                string temp = str.Substring(index + 5).Trim();
                result.Add(temp);
            }
            return result.ToArray();
        }

        /// <summary>
        /// 判断当前目录下指定的子目录是否存在，只能判断单级目录
        /// </summary>
        /// <param name="RemoteDirectoryName">指定的目录名</param>
        public bool DirectoryExist(string RemoteDirectoryName)
        {
            string[] dirList = GetDirectoryList();
            foreach (string str in dirList)
            {
                if (str.Trim() == RemoteDirectoryName.Trim())
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 判断当前目录下指定的文件是否存在，支持多级目录
        /// </summary>
        /// <param name="RemoteFileName">远程文件名</param>
        public bool FileExist(string RemoteFileName)
        {
            FileInfo fileinformation = new FileInfo(RemoteFileName);
            int index = RemoteFileName.LastIndexOf('/');//检查RemoteFileName是否包含多级目录
            if (-1 != index)
            {
                string currPath = RemoteFileName.Substring(0, index);
                MakeDirs(currPath);//保证指定目录存在
            }
            string[] fileList = GetFileList();//获取文件列表
            if (-1 != index)
                GotoDirectory("", true);//返回root目录
            foreach (string str in fileList)
            {
                if (str.Trim() == fileinformation.Name)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 创建文件夹，单级
        /// </summary>
        /// <param name="dirName"></param>
        public void MakeDir(string dirName)
        {
            FtpWebRequest reqFTP;
            try
            {
                // dirName = name of the directory to create.
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName));
                reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
                reqFTP.UseBinary = true;
                reqFTP.UsePassive = false;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                Stream ftpStream = response.GetResponseStream();

                ftpStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw new Exception("FtpHelper MakeDir Error --> " + ex.Message);
            }
        }
        /// <summary>
        /// 创建并进入目录，支持多级目录
        /// </summary>
        /// <param name="dirName"></param>
        public void MakeDirs(string dirName)
        {
            if (dirName == string.Empty)
                return;
            GotoDirectory("", true);//返回root路径
            char[] splitChar = new char[] { '/' };
            string[] temps = dirName.Split(splitChar);
            try
            {
                for (int index = 0; index < temps.Length; index++)
                {
                    if (!DirectoryExist(temps[index]))
                    {
                        MakeDir(temps[index]);
                    }
                    GotoDirectory(temps[index], false);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取指定文件大小，支持多级路径
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public long GetFileSize(string filename)
        {
            if (!FileExist(filename))
                return -1;
            long fileSize;
            FtpWebRequest reqFTP;
            try
            {
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + filename));
                reqFTP.Method = WebRequestMethods.Ftp.GetFileSize;
                reqFTP.UseBinary = true;
                reqFTP.UsePassive = false;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                Stream ftpStream = response.GetResponseStream();
                fileSize = response.ContentLength;

                ftpStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw new Exception("FtpHelper GetFileSize Error --> " + ex.Message);
            }
            return fileSize;
        }

        /// <summary>
        /// 改名
        /// </summary>
        /// <param name="currentFilename"></param>
        /// <param name="newFilename"></param>
        public void ReName(string currentFilename, string newFilename)
        {
            FtpWebRequest reqFTP;
            try
            {
                reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + currentFilename));
                reqFTP.Method = WebRequestMethods.Ftp.Rename;
                reqFTP.RenameTo = newFilename;
                reqFTP.UseBinary = true;
                reqFTP.UsePassive = false;
                reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
                FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                Stream ftpStream = response.GetResponseStream();

                ftpStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw new Exception("FtpHelper ReName Error --> " + ex.Message);
            }
        }

        /// <summary>
        /// 移动文件
        /// </summary>
        /// <param name="currentFilename"></param>
        /// <param name="newFilename"></param>
        public void MovieFile(string currentFilename, string newDirectory)
        {
            ReName(currentFilename, newDirectory);
        }

        /// <summary>
        /// 切换当前目录
        /// </summary>
        /// <param name="DirectoryName"></param>
        /// <param name="IsRoot">true 绝对路径   false 相对路径</param>
        public void GotoDirectory(string DirectoryName, bool IsRoot)
        {
            if (IsRoot)
            {
                ftpRemotePath = DirectoryName;
            }
            else
            {
                ftpRemotePath += DirectoryName + "/";
            }
            ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/";
        }
    }
}
